/*
********************************************************************************
*
*	file : update
*
********************************************************************************
*/
#include "dataproc.h"
#include "stdio.h"
#include "mytype.h"
#include "swap.h"
#include "mbcom.h"
#include "str_ext.h"
#include "app_update.h"
#include "common.h"

/********************************************************************************
*                               cfg
********************************************************************************/
// 文件格式
// 块   原文   原文CRC  密文CRC  全文LRC
// n * (1024  +  2   +   2)   +   4
#define CODE_PAGE_SIZE 1024UL

#define APP_FLASH_BASE iap_obj.app_mem_base
#define APP_FLASH_SIZE iap_obj.app_mem_size
#define APP_ID_MEM_OFFSET iap_obj.id_mem_offset
#define APP_ID_MEM_VALUE iap_obj.id_mem_value

/********************************************************************************
*                               fun
********************************************************************************/
void swap_iap_opt(int opt);
void swap_iap_info_out(void);
void swap_iap_flash_end(void);
void swap_iap_flash(uint32_t offset);
void swap_iap_dat_dec(void);
uint8_t is_swap_iap_flash_different(uint32_t offset);

/*******************************************************************************
* @brief  main : std proc entry.
* \param[in] none
* \retval: none
*******************************************************************************/
void swap_iap_update(uint8_t check0_or_program1)
{
	int cnt;

	ap_ext_start();
	iap_obj.out.st = 0;

	//升级文件检测
	cnt = 3;
	while (cnt--)
	{
		swap_iap_opt(0);

		if (!iap_obj.err.st)
		{
			break;
		}
	}

	//升级
	if (check0_or_program1)
	{
		iap_obj.out.bits.update_req = 1;

		if (!iap_obj.err.st && iap_obj.out.bits.app_diff)
		{
			cnt = 3;
			while (cnt--)
			{
				swap_iap_opt(1);

				if (!iap_obj.err.st)
				{
					break;
				}
			}
		}
	}

	//结果输出
	swap_iap_info_out();

	ap_ext_end();
}

/*******************************************************************************
* @brief  swap_iap_opt
* \param[in] write_en: 0-juat check, 1-check and write
* \retval: none
*******************************************************************************/
void swap_iap_opt(int write_en)
{
	int index;
	uint32_t len_read_total;
	uint32_t sum_cal, sum_rd[2];
	uint16_t crc_cal, crc_rd[4];

	iap_obj.err.st = 0;

	dbg_puts("swap iap opt start :: \n");
	
	//检测程序是否有效
	do
	{
		//打开文件
		if (!ap_file_open(iap_obj.src_file, FIL_OPEN_R))
		{
			iap_obj.err.bits.err_no_file = 1;
			break;
		}

		iap_obj.enc_file_size = ap_file_size_get();

		// 文件尺寸检查
		if ((iap_obj.enc_file_size - 4) % (CODE_PAGE_SIZE + 4) != 0)
		{
			iap_obj.err.bits.err_size = 1;
			break;
		}

		if (iap_obj.enc_file_size == 0)
		{
			iap_obj.err.bits.err_size = 1;
			break;
		}

		// app bin 尺寸
		iap_obj.app_bin_size = (iap_obj.enc_file_size - 4) / (CODE_PAGE_SIZE + 4);
		iap_obj.app_bin_size *= CODE_PAGE_SIZE;
		if (iap_obj.app_bin_size > APP_FLASH_SIZE)
		{
			iap_obj.err.bits.err_size = 1;
			break;
		}

		//读取检测
		len_read_total = 0;
		sum_cal = 0;

		for (;len_read_total < (iap_obj.enc_file_size - 4); len_read_total += CODE_PAGE_SIZE + 4)
		{
			memset(iap_obj.buff, 0xff, CODE_PAGE_SIZE);

			//读取一页数据
			if (0 == ap_file_read(iap_obj.buff, CODE_PAGE_SIZE + 4))
			{
				iap_obj.err.bits.err_file_read = 1;
				break;
			}
			//全文sum
			sum_cal += *((uint32_t *)(&iap_obj.buff[CODE_PAGE_SIZE]));
			
			//提取原crc
			memcpy(crc_rd, &iap_obj.buff[CODE_PAGE_SIZE], 4);

			//enc crc
			crc_cal = mb_crc((uint8_t *)iap_obj.buff, CODE_PAGE_SIZE);
			if (crc_rd[1] != crc_cal)
			{
				iap_obj.err.bits.err_crc = 1;
				break;
			}

			//密文解算
			swap_iap_dat_dec();

			//src crc
			crc_cal = mb_crc((uint8_t *)iap_obj.buff, 1024);
			if (crc_rd[0] != crc_cal)
			{
				iap_obj.err.bits.err_crc = 1;
				break;
			}

			// 程序ID
			if ((len_read_total == 0) && (APP_ID_MEM_OFFSET))
			{
				uint32_t *p_id;
				p_id = (uint32_t *)&iap_obj.buff[APP_ID_MEM_OFFSET];

				if (*p_id != APP_ID_MEM_VALUE)
				{
					iap_obj.err.bits.id_err = 1;
				}
			}

			//flash编程
			if (write_en)
			{
				if (!iap_obj.err.st)
				{
					swap_iap_flash(len_read_total / (CODE_PAGE_SIZE + 4) * CODE_PAGE_SIZE);
					if((len_read_total / (CODE_PAGE_SIZE + 4) & 0x0F) == 0)
					{
						dbg_print("swap iap : %d %%\n", len_read_total * 100 / iap_obj.enc_file_size);
					}
				}
			}
			else
			{
				if (is_swap_iap_flash_different(len_read_total / (CODE_PAGE_SIZE + 4) * CODE_PAGE_SIZE))
				{
					iap_obj.out.bits.app_diff = 1;
				}
			}
		}

		//读sum
		if (0 == ap_file_read(sum_rd, 4))
		{
			iap_obj.err.bits.err_sum = 1;
			break;
		}

		//累加和
		if (sum_rd[0] != sum_cal)
		{
			iap_obj.err.bits.err_sum = 1;
		}

		//错误退出
		if (iap_obj.err.st)
		{
			break;
		}
	} while (0);

	ap_file_close();

	if (iap_obj.err.st)
	{
		iap_obj.out.bits.app_diff = 1;
	}

	//升级完成操作
	if (iap_obj.err.st == 0)
	{
		if (write_en)
		{
			swap_iap_flash_end();
		}
	}
	
	dbg_puts("swap iap opt end !\n");
}

/*******************************************************************************
* @brief  swap_iap_dat_dec : 解码
* \param[in] none
* \retval: none
*******************************************************************************/
void swap_iap_dat_dec(void)
{
	int index;

	// 位序反转
	swap_u32_with_bit(iap_obj.buff, iap_obj.buff, CODE_PAGE_SIZE / 4);

	// 字节序反转
	swap_list_with_u8(&iap_obj.buff[120], &iap_obj.buff[120], 800);

	// 位图转置
	for (index = 0; index < CODE_PAGE_SIZE / 128; index++)
		swap_bit_map_u32(&iap_obj.buff[128 * index], &iap_obj.buff[128 * index]);

	// 半字节序反转
	swap_u16_with_u4(iap_obj.buff, iap_obj.buff, CODE_PAGE_SIZE / 2);
}

/*******************************************************************************
* @brief  main : 检测一页数据是否完全相同
* \param[in] offset 偏移地址
* \retval: diff
*******************************************************************************/
uint8_t is_swap_iap_flash_different(uint32_t offset)
{
	//读取
	ap_flash_read(APP_FLASH_BASE + offset, CODE_PAGE_SIZE, iap_obj.comm_buff);

	return comp_i32(iap_obj.buff, iap_obj.comm_buff, CODE_PAGE_SIZE >> 2);
}

/*******************************************************************************
* @brief  main : swap_iap_flash. 编程一页数据
* \param[in] none
* \retval: none
*******************************************************************************/
void swap_iap_flash(uint32_t offset)
{
	// 开始编程时擦除
	if (offset == 0)
	{
		copy_u32(iap_obj.buff, iap_obj.head_buff, CODE_PAGE_SIZE / 4);
		ap_flash_erase(APP_FLASH_BASE, APP_FLASH_BASE + iap_obj.app_bin_size - 1);
	}

	//首页不直接编程
	if (offset != 0)
	{
		ap_flash_write(APP_FLASH_BASE + offset, CODE_PAGE_SIZE, iap_obj.buff);

		if (is_swap_iap_flash_different(offset))
		{
			iap_obj.err.bits.verify = 1;
		}
	}
}

/*******************************************************************************
* @brief  main : swap_iap_flash_end.
* \param[in] none
* \retval: none
*******************************************************************************/
void swap_iap_flash_end(void)
{
	// 编程第一页
	ap_flash_write(APP_FLASH_BASE, CODE_PAGE_SIZE, iap_obj.head_buff);
	copy_u32(iap_obj.head_buff, iap_obj.buff, CODE_PAGE_SIZE / 4);
	
	if (is_swap_iap_flash_different(0))
	{
		iap_obj.err.bits.verify = 1;
	}
}

/*******************************************************************************
* @brief  main : swap_iap_info_out.
* \param[in] none
* \retval: none
*******************************************************************************/
void swap_iap_info_out(void)
{
	int index;

	// 无升级文件时不输出
	if (iap_obj.err.bits.err_no_file)
		return;

	if (ap_file_open(iap_obj.out_file, FIL_OPEN_WA))
	{
		ap_file_seek(ap_file_size_get());

		index = 0;

		index = sprintf((char *)&iap_obj.buff[0], "\r\n...........................................................\r\n");
		ap_file_write(iap_obj.buff, index);

		if (0 == iap_obj.err.st)
		{
			index = 0;

			if (iap_obj.out.bits.update_req)
			{
				// 检测并升级
				index += sprintf((char *)&iap_obj.buff[index], "Uptade success!\r\n");

				if (!iap_obj.out.bits.app_diff)
				{
					// BIN文件相同
					index += sprintf((char *)&iap_obj.buff[index], "The app file is the same as the input file! \r\n");
				}
				else
				{
					// 更新成功
					index += sprintf((char *)&iap_obj.buff[index], "Uptade file size %d Bytes!\r\n", iap_obj.enc_file_size);
					index += sprintf((char *)&iap_obj.buff[index], "Uptade bin  size %d Bytes!\r\n", iap_obj.app_bin_size);
				}
				
				iap_obj.out.bits.update_ok = 1;
			}
			else
			{
				// 仅检测
				index += sprintf((char *)&iap_obj.buff[index], "Check success!\r\n");
				if (iap_obj.out.bits.app_diff)
				{
					// 文件检查通过
					index += sprintf((char *)&iap_obj.buff[index], "Uptade file size %d Bytes!\r\n", iap_obj.enc_file_size);
					index += sprintf((char *)&iap_obj.buff[index], "Uptade bin  size %d Bytes!\r\n", iap_obj.app_bin_size);
				}
				else
				{
					// 文件相同
					index += sprintf((char *)&iap_obj.buff[index], "The app file is the same as the input file! \r\n");
				}
				iap_obj.out.bits.check_ok = 1;
			}
			
			ap_file_write(iap_obj.buff, index);
			dbg_puts((char *)iap_obj.buff);
		}
		else
		{
			if (iap_obj.out.bits.update_req)
			{
				iap_obj.out.bits.update_err = 1;
			}
			
			index = sprintf((char *)&iap_obj.buff[0], "UPDATE OR CHECK ERROR::\r\n");
			ap_file_write(iap_obj.buff, index);
			dbg_puts((char *)iap_obj.buff);
			
			if (iap_obj.err.bits.err_no_file)
			{
				index = sprintf((char *)&iap_obj.buff[0], "No file found! -> yt_app.bin \r\n");
				ap_file_write(iap_obj.buff, index);
			}

			if (iap_obj.err.bits.err_crc)
			{
				index = sprintf((char *)&iap_obj.buff[0], "The file crc is error! \r\n");
				ap_file_write(iap_obj.buff, index);
			}

			if (iap_obj.err.bits.err_sum)
			{
				index = sprintf((char *)&iap_obj.buff[0], "The app file is incomplete! \r\n");
				ap_file_write(iap_obj.buff, index);
			}

			if (iap_obj.err.bits.verify)
			{
				index = sprintf((char *)&iap_obj.buff[0], "The app flash verify fail! \r\n");
				ap_file_write(iap_obj.buff, index);
			}

			if (iap_obj.err.bits.err_size)
			{
				index = sprintf((char *)&iap_obj.buff[0], "The app file size is err! \r\n");
				ap_file_write(iap_obj.buff, index);
			}

			if (iap_obj.err.bits.err_file_read)
			{
				index = sprintf((char *)&iap_obj.buff[0], "The app file file read err! \r\n");
				ap_file_write(iap_obj.buff, index);
			}

			if (!iap_obj.err.st && iap_obj.err.bits.id_err)
			{
				index = sprintf((char *)&iap_obj.buff[0], "The app program id is not match! \r\n");
				ap_file_write(iap_obj.buff, index);
			}
		}
	}

	index = sprintf((char *)&iap_obj.buff[0], "...........................................................\r\n");
	ap_file_write(iap_obj.buff, index);

	ap_file_close();
}
